package ift605.EquationWithAgent.server.agent;

import ift605.EquationWithAgent.server.EquationMessage;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import udes.ds.agent.AbstractEquation;
import udes.ds.agent.SummativeEquation;

import jade.core.AID;
import jade.core.behaviours.CyclicBehaviour;
import jade.lang.acl.ACLMessage;
import jade.lang.acl.MessageTemplate;
import jade.lang.acl.UnreadableException;

public class PolynomialEquationAgent extends DeriveSlaveAgent {
	private static final long serialVersionUID = 1L;
	protected int mNextRequest;
	protected Map<String, PolynomialInDerivation> mInProgress;
	
	protected void setup() {
		super.setup();
		mNextRequest = 0; // Message ID for a request
		mInProgress = new HashMap<String, PolynomialEquationAgent.PolynomialInDerivation>(); // BD
		addBehaviour(new DerivateBehaviour());
		addBehaviour(new ProcessResultBehaviour());
	}
	
	private class DerivateBehaviour extends CyclicBehaviour{
		private static final long serialVersionUID = 1L;

		@Override
		public void action() {
			ACLMessage request = null;
			MessageTemplate mt = MessageTemplate.MatchConversationId(EquationMessage.DERIVATE_ID);
			request = receive(mt);
			if(request != null){
				try {
					EquationMessage equationRequest = (EquationMessage)request.getContentObject();
					SummativeEquation casted = (SummativeEquation)equationRequest.getEquation();			
					
					ACLMessage response = new ACLMessage(ACLMessage.INFORM);
					response.addReceiver(new AID("Derivative", AID.ISLOCALNAME));
					response.setConversationId(EquationMessage.DERIVATE_ID);
					
					// Send first element
					response.setContentObject(new EquationMessage(mNextRequest, casted.getFirst(), 1));
					send(response); 
					
					// Send second element
					response.setContentObject(new EquationMessage(mNextRequest, casted.getSecond(), 2));
					send(response);
					
					mInProgress.put(Integer.toString(mNextRequest), new PolynomialInDerivation(request.getSender(),
								 											equationRequest.getRequestNumber(), 
								 											equationRequest.getIdentifier()));
					
					// Increase the message ID for the next request
					mNextRequest += 1;
				}
				catch (IOException e) {
					e.printStackTrace();
				}
				catch (UnreadableException e) {
					e.printStackTrace();
				}
			}
		}
	}
	
	private class ProcessResultBehaviour extends CyclicBehaviour{
		private static final long serialVersionUID = 1L;

		@Override
		public void action() {
			ACLMessage request = null;
			MessageTemplate mt = MessageTemplate.MatchConversationId(EquationMessage.RESULT_ID);
			request = receive(mt);
			if(request != null){
				try {
					// Add in the BD the part of the result we received
					EquationMessage equationResult = (EquationMessage)request.getContentObject();
					int requestNumber = equationResult.getRequestNumber();
					int partNumber = equationResult.getIdentifier();
					AbstractEquation derivateEquation = equationResult.getEquation();
					((PolynomialInDerivation)(mInProgress.get(Integer.toString(requestNumber)))).answer(partNumber, derivateEquation);
					PolynomialInDerivation poly = (PolynomialInDerivation)(mInProgress.get(Integer.toString(requestNumber)));
					
					// if we received all part of the requests, return the answer to the requester
					if(poly.isComplete()){
						ACLMessage response = new ACLMessage(ACLMessage.INFORM);
						response.addReceiver(poly.getSender());
						response.setConversationId(EquationMessage.RESULT_ID);
						AbstractEquation result = new SummativeEquation( poly.getDerivatePart1(), poly.getDerivatePart2());
						response.setContentObject(new EquationMessage(poly.getRequestNumber(), result, poly.getIdentifier())); 
						send(response); 
						
						// Remove the resquest from the BD
						mInProgress.remove(Integer.toString(requestNumber));
					}
				}
				catch (IOException e) {
					e.printStackTrace();
				}
				catch (UnreadableException e) {
					e.printStackTrace();
				}
			}
		}
	}
	
	// Structure of a request
	private class PolynomialInDerivation{
		private AbstractEquation mDerivatePart1;
		private AbstractEquation mDerivatePart2;
		private AID mSender;
		private int mRequestNumber;
		private int mIdentifier;
		
		public PolynomialInDerivation(AID aSender, int aRequestNumber, int aIdentifier){
			mDerivatePart1 = null;
			mDerivatePart2 = null;
			mSender = aSender;
			mRequestNumber = aRequestNumber;
			mIdentifier = aIdentifier;
		}
		
		public void answer(int aPartNumber, AbstractEquation aDerivateEquation){
			switch(aPartNumber){
			case 1:
				mDerivatePart1 = aDerivateEquation;
				break;
			case 2:
				mDerivatePart2 = aDerivateEquation;
				break;
			}
		}
		
		public boolean isComplete(){
			return mDerivatePart1 != null && mDerivatePart2 != null;
		}
		
		public AbstractEquation getDerivatePart1() {
			return mDerivatePart1;
		}

		public AbstractEquation getDerivatePart2() {
			return mDerivatePart2;
		}

		public AID getSender() {
			return mSender;
		}
		
		public int getRequestNumber(){
			return mRequestNumber;
		}
		
		public int getIdentifier(){
			return mIdentifier;
		}
	}

	@Override
	public Class<?> GetDeriveClass() {
		return SummativeEquation.class;
	}
}
